{{!

  Copyright (c) Meta Platforms, Inc. and affiliates.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

}}
{{#import "method_decorators/params" as decorator_params}}
{{#import "method_decorators/decorator_cpp" as decorator_cpp}}
{{#service:functions?}}
//
// Service Methods
//

{{/service:functions?}}
{{#service:functions}}
{{#let in_or_creates_interaction? = (or service:interaction? function:creates_interaction?)}}
{{#function:return_type}}
{{^function:starts_interaction?}}
//
// Method '{{function:name}}'
//
template <typename ProtocolIn_, typename ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::setUpAndProcess_{{function:prefixed_name}}(
    apache::thrift::ResponseChannelRequest::UniquePtr req,
    apache::thrift::SerializedCompressedRequest&& serializedRequest,
    apache::thrift::Cpp2RequestContext* ctx,
    folly::EventBase* eb,
    [[maybe_unused]] apache::thrift::concurrency::ThreadManager* tm) {
  if (!setUpRequestProcessing(
          req, ctx, eb, {{#if function:eb}}nullptr{{#else}}tm{{/if}}, {{> types/function_kind}}, iface_{{#if service:interaction?}}, "{{interaction:name}}"{{/if}}{{#if function:creates_interaction?}}, "{{function:created_interaction.cpp_name}}", true{{/if}})) {
    return;
  }
{{^function:eb}}
  auto scope = iface_->getRequestExecutionScope(
      ctx, apache::thrift::concurrency::{{function:priority}});
  ctx->setRequestExecutionScope(std::move(scope));
  processInThread(
      std::move(req),
      std::move(serializedRequest),
      ctx,
      eb,
      tm,
      {{> types/function_kind}},
      &{{service:parent_service_cpp_name}}AsyncProcessor::
          executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
      this);
{{/function:eb}}{{#function:eb}}
  {{#if in_or_creates_interaction?}}
  processInThread(
      std::move(req),
      std::move(serializedRequest),
      ctx,
      eb,
      nullptr,
      {{> types/function_kind}},
      &{{service:parent_service_cpp_name}}AsyncProcessor::
          executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
      this);
  {{#else}}
  {{^function:oneway?}}
  if (!req->getShouldStartProcessing()) {
    apache::thrift::HandlerCallbackBase::releaseRequest(
        std::move(req),
        eb);
    return;
  }
  {{/function:oneway?}}
  apache::thrift::ServerRequest serverRequest{
      std::move(req),
      std::move(serializedRequest),
      ctx,
      {},
      {},
      {},
      {}};
  executeRequest_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>(std::move(serverRequest));
  {{/if in_or_creates_interaction?}}
{{/function:eb}}
}

template <typename ProtocolIn_, typename ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::executeRequest_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}(
    apache::thrift::ServerRequest&& serverRequest) {
{{#if in_or_creates_interaction?}}
  auto tile = serverRequest.requestContext()->releaseTile();
{{/if in_or_creates_interaction?}}
  // make sure getRequestContext is null
  // so async calls don't accidentally use it
  iface_->setRequestContext(nullptr);
  struct ArgsState {
{{#function:args}}{{#field:type}}
    {{^type:resolves_to_complex_return?}}
    {{^type:enum?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}}{0};
    {{/type:enum?}}
    {{#type:enum?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}}{static_cast<{{type:cpp_type}}>(0)};
    {{/type:enum?}}
    {{/type:resolves_to_complex_return?}}
    {{#type:resolves_to_complex_return?}}
    {{#function:stack_arguments?}}
    {{type:cpp_type}} uarg_{{field:cpp_name}};
    {{/function:stack_arguments?}}
    {{^function:stack_arguments?}}
    std::unique_ptr<{{type:cpp_type}}> uarg_{{field:cpp_name}} = std::make_unique<{{type:cpp_type}}>();
    {{/function:stack_arguments?}}
    {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
    {{service:parent_service_cpp_name}}_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}_pargs pargs() {
      {{service:parent_service_cpp_name}}_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}_pargs args;
{{#function:args}}{{#field:type}}
      {{^type:resolves_to_complex_return?}}
      args.get<{{field:index}}>().value = &uarg_{{field:cpp_name}};
      {{/type:resolves_to_complex_return?}}
      {{#type:resolves_to_complex_return?}}
      {{#function:stack_arguments?}}
      args.get<{{field:index}}>().value = &uarg_{{field:cpp_name}};
      {{/function:stack_arguments?}}
      {{^function:stack_arguments?}}
      args.get<{{field:index}}>().value = uarg_{{field:cpp_name}}.get();
      {{/function:stack_arguments?}}
      {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
      return args;
    }

    auto asTupleOfRefs() & {
      return std::tie(
{{#function:args}}{{#field:type}}
        {{^type:resolves_to_complex_return?}}
        std::as_const(uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/type:resolves_to_complex_return?}}
        {{#type:resolves_to_complex_return?}}
        {{#function:stack_arguments?}}
        std::as_const(uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/function:stack_arguments?}}
        {{^function:stack_arguments?}}
        std::as_const(*uarg_{{field:cpp_name}}){{^last?}},{{/last?}}
        {{/function:stack_arguments?}}
        {{/type:resolves_to_complex_return?}}
{{/field:type}}{{/function:args}}
      );
    }
  } args;

  auto ctxStack = apache::thrift::ContextStack::create(
      this->getEventHandlersSharedPtr(),
      this->getServiceName(),
      "{{service:parent_service_name}}.{{> common/function_name}}",
      serverRequest.requestContext());
{{#service:interaction?}}
  auto& iface = static_cast<apache::thrift::ServiceHandler<{{service:parent_service_cpp_name}}>::{{service:name}}If&>(*tile);
{{/service:interaction?}}
  apache::thrift::SerializedRequest serializedRequest{nullptr};
  try {
    auto pargs = args.pargs();
    serializedRequest = apache::thrift::detail::ServerRequestHelper::compressedRequest(
        std::move(serverRequest)).uncompress();
    deserializeRequest<ProtocolIn_>(
        pargs,
        "{{> common/function_name}}",
        serializedRequest,
        ctxStack.get());
  } catch (...) {
{{#function:oneway?}}
    LOG(ERROR) << "exception in function {{> common/function_name}}: " << folly::exceptionStr(std::current_exception());
    apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest)->runInEventBaseThread(
        [req = apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest))] {});
    return;
  }
  auto requestPileNotification = apache::thrift::detail::ServerRequestHelper::moveRequestPileNotification(serverRequest);
  auto concurrencyControllerNotification =
      apache::thrift::detail::ServerRequestHelper::moveConcurrencyControllerNotification(
          serverRequest);
  apache::thrift::HandlerCallbackBase::MethodNameInfo methodNameInfo{
      /* .serviceName =*/ this->getServiceName(),
      /* .definingServiceName =*/ "{{service:parent_service_name}}",
      /* .methodName =*/ "{{> common/function_name}}",
      /* .qualifiedMethodName =*/ "{{#if service:interaction?}}{{service:parent_service_name}}{{#else}}{{service:name}}{{/if service:interaction?}}.{{> common/function_name}}"};
{{#if service:self.interaction?}}
  apache::thrift::HandlerCallbackOneWay::DecoratorAfterCallback decoratorCallback{
    // apache::thrift::detail::TileInternalAPI(iface).getDecoratorIface(),
    static_cast<void*>(iface_),
    apache::thrift::ServiceHandler<{{service:parent_service_cpp_name}}>::fbthrift_invoke_decorator_after_{{service:name}}_{{function:self.cpp_name}}};
{{#else}}
  apache::thrift::HandlerCallbackOneWay::DecoratorAfterCallback decoratorCallback{
    static_cast<void*>(iface_),
    apache::thrift::ServiceHandler<{{service:self.qualified_name}}>::fbthrift_invoke_decorator_after_{{function:self.cpp_name}}};
{{/if service:self.interaction?}}
  auto callback = std::make_unique<apache::thrift::HandlerCallbackOneWay>(
      apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest)),
      std::move(ctxStack),
      std::move(methodNameInfo),
      nullptr /* exceptionFuncPointer */,
      apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
      apache::thrift::detail::ServerRequestHelper::executor(serverRequest),
      serverRequest.requestContext(),
      requestPileNotification,
      concurrencyControllerNotification,
      std::move(serverRequest.requestData()),
      {{#if in_or_creates_interaction?}}std::move(tile), {{#else}}apache::thrift::TilePtr(),{{/if in_or_creates_interaction?}}
      std::move(decoratorCallback));
{{/function:oneway?}}
{{^function:oneway?}}
    folly::exception_wrapper ew(std::current_exception());
    apache::thrift::detail::ap::process_handle_exn_deserialization<
        ProtocolOut_>(
        ew,
        apache::thrift::detail::ServerRequestHelper::request(std::move(serverRequest)),
            serverRequest.requestContext(),
        apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
        "{{> common/function_name}}");
    return;
  }
  auto requestPileNotification =
      apache::thrift::detail::ServerRequestHelper::moveRequestPileNotification(
          serverRequest);
  auto concurrencyControllerNotification =
      apache::thrift::detail::ServerRequestHelper::moveConcurrencyControllerNotification(
          serverRequest);
  apache::thrift::HandlerCallbackBase::MethodNameInfo methodNameInfo{
      /* .serviceName =*/ this->getServiceName(),
      /* .definingServiceName =*/ "{{service:parent_service_name}}",
      /* .methodName =*/ "{{> common/function_name}}",
      /* .qualifiedMethodName =*/ "{{#if service:interaction?}}{{service:parent_service_name}}{{#else}}{{service:name}}{{/if service:interaction?}}.{{> common/function_name}}"};
{{#if service:self.interaction?}}
  apache::thrift::HandlerCallback<{{> types/return_type_server}}>::DecoratorAfterCallback decoratorCallback{
    static_cast<void*>(iface_),
    apache::thrift::ServiceHandler<{{service:parent_service_cpp_name}}>::fbthrift_invoke_decorator_after_{{service:name}}_{{function:self.cpp_name}}};
{{#else}}
  apache::thrift::HandlerCallback<{{> types/return_type_server}}>::DecoratorAfterCallback decoratorCallback{
    static_cast<void*>(iface_),
    apache::thrift::ServiceHandler<{{service:self.qualified_name}}>::fbthrift_invoke_decorator_after_{{function:self.cpp_name}}};
{{/if service:self.interaction?}}
 auto callback =
      apache::thrift::HandlerCallbackPtr<{{> types/return_type_server}}>::make(
          apache::thrift::detail::ServerRequestHelper::request(
              std::move(serverRequest)),
          std::move(ctxStack),
          std::move(methodNameInfo),
          return_{{function:prefixed_name}}<ProtocolIn_, ProtocolOut_>,
          throw_wrapped_{{#service:interaction?}}{{service:name}}_{{/service:interaction?}}{{function:cpp_name}}<ProtocolIn_, ProtocolOut_>,
          serverRequest.requestContext()->getProtoSeqId(),
          apache::thrift::detail::ServerRequestHelper::eventBase(serverRequest),
          {{^function:eb}}apache::thrift::detail::ServerRequestHelper::executor(serverRequest){{/function:eb}}{{#function:eb}}nullptr{{/function:eb}},
          serverRequest.requestContext(),
          requestPileNotification,
          concurrencyControllerNotification,
          std::move(serverRequest.requestData()),
          {{#if in_or_creates_interaction?}}std::move(tile),{{#else}}apache::thrift::TilePtr(),{{/if in_or_creates_interaction?}}
          std::move(decoratorCallback));
{{/function:oneway?}}
{{#if service:self.interaction?}}
  // Execute method decorator before_{{service:name}}_{{function:cpp_name}}.
  iface_->fbthrift_execute_decorators_before_{{service:name}}_{{function:self.cpp_name}}(*serverRequest.requestContext(){{#partial decorator_params.before_invocation_args function=function:self}});
{{#else}}
  // Execute method decorator before_{{function:cpp_name}}.
  iface_->fbthrift_execute_decorators_before_{{function:self.cpp_name}}(*serverRequest.requestContext(){{#partial decorator_params.before_invocation_args function=function:self}});

{{/if service:self.interaction?}}
  const auto makeExecuteHandler = [&] {
    return [ifacePtr = {{^service:interaction?}}iface_{{/service:interaction?}}{{#service:interaction?}}&iface{{/service:interaction?}}](auto&& cb, ArgsState args) mutable {
      (void)args;
{{#if service:self.interaction?}}
      if (ifacePtr == nullptr) {
        cb->complete(folly::Try<{{> types/return_type_server}}>(apache::thrift::detail::si::create_app_exn_bad_interaction_state("{{service:name}}", "{{function:name}}")));
        return;
      }
{{/if service:self.interaction?}}
      ifacePtr->{{#function:eb}}async_eb{{/function:eb}}{{^function:eb}}async_tm{{/function:eb}}_{{function:cpp_name}}(std::move(cb){{> service_tcc/get_args_ref}});
    };
  };
#if FOLLY_HAS_COROUTINES
  if (apache::thrift::detail::shouldProcessServiceInterceptorsOnRequest(
          *callback)) {
    [](
        auto callback,
        auto executeHandler,
        ArgsState args,
        apache::thrift::SerializedRequest serializedRequest
    ) -> folly::coro::Task<void> {
      auto argRefs = args.asTupleOfRefs();
      co_await apache::thrift::detail::processServiceInterceptorsOnRequest(
          *callback,
          apache::thrift::detail::ServiceInterceptorOnRequestArguments(argRefs),
          serializedRequest);
      executeHandler(std::move(callback), std::move(args));
    }(
        std::move(callback),
        makeExecuteHandler(),
        std::move(args),
        std::move(serializedRequest))
      .scheduleOn(apache::thrift::detail::ServerRequestHelper::executor(serverRequest))
      .startInlineUnsafe();
  } else {
    makeExecuteHandler()(std::move(callback), std::move(args));
  }
#else
  makeExecuteHandler()(std::move(callback), std::move(args));
#endif // FOLLY_HAS_COROUTINES
}

{{^function:oneway?}}
template <class ProtocolIn_, class ProtocolOut_>
{{^function:cpp_recv_arg?}}
/* static */ apache::thrift::SerializedResponse {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx) {
{{/function:cpp_recv_arg?}}
{{#function:cpp_recv_arg?}}
{{^function:stream?}}{{^function:sink?}}
/* static */ apache::thrift::SerializedResponse {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    {{function:cpp_return_type}} const& _return) {
{{/function:sink?}}{{/function:stream?}}
{{#if function:self.bidirectional_stream?}}
/* static */ apache::thrift::ResponseAndServerBiDiStreamFactory {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    folly::Executor::KeepAlive<> executor,
    {{function:cpp_return_type}}&& _return) {
{{#else if function:self.stream?}}
/* static */ apache::thrift::ResponseAndServerStreamFactory {{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    folly::Executor::KeepAlive<> executor,
    {{function:cpp_return_type}}&& _return) {
{{#else if function:self.sink?}}
/* static */ std::pair<
    apache::thrift::SerializedResponse,
    apache::thrift::detail::SinkConsumerImpl>
{{service:parent_service_cpp_name}}AsyncProcessor::return_{{function:prefixed_name}}(
    apache::thrift::ContextStack* ctx,
    {{function:cpp_return_type}}&& _return,
    folly::Executor::KeepAlive<> executor) {
{{/if function:self.bidirectional_stream?}}
{{/function:cpp_recv_arg?}}
  ProtocolOut_ prot;
{{#if function:bidirectional_stream?}}
  {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::InitialResponsePResultType result;
  using SinkPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::SinkPResultType;
  using StreamPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::StreamPResultType;
{{#if function:initial_response?}}
  result.get<0>().value = const_cast<{{function:cpp_return_type}}::ResponseType*>(&_return.response);
  result.setIsSet(0, true);
  auto bidiStreamFactory = apache::thrift::detail::ap::encode_server_bidi_stream<
    ProtocolIn_, SinkPResultType, ProtocolOut_, StreamPResultType>(
      std::move(_return.transform), std::move(executor));
  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(bidiStreamFactory)};
{{#else}}
  auto bidiStreamFactory = apache::thrift::detail::ap::encode_server_bidi_stream<
    ProtocolIn_, SinkPResultType, ProtocolOut_, StreamPResultType>(
      std::move(_return), std::move(executor));
  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(bidiStreamFactory)};
{{/if function:initial_response?}}
{{#else if function:stream?}}
  {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::InitialResponsePResultType result;
  using StreamPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::StreamPResultType;
{{#if function:stream_has_first_response?}}
  result.get<0>().value = const_cast<{{function:cpp_return_type}}::ResponseType*>(&_return.response);
  result.setIsSet(0, true);
  auto& returnStream = _return.stream;
{{#else}}
  auto& returnStream = _return;
{{/if function:stream_has_first_response?}}
  auto encodedStream = apache::thrift::detail::ap::encode_server_stream<ProtocolOut_, StreamPResultType>(std::move(returnStream), std::move(executor));
  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(encodedStream)};
{{#else if function:sink?}}
  {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::InitialResponsePResultType result;
  using SinkPResultType = {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::SinkPResultType;
  using FinalResponsePResultType =
      {{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult::FinalResponsePResultType;
{{#if function:sink_has_first_response?}}
  result.get<0>().value = &_return.response;
  result.setIsSet(0, true);
{{/if function:sink_has_first_response?}}
  auto sinkConsumerImpl = apache::thrift::detail::ap::toSinkConsumerImpl<
      ProtocolIn_,
      ProtocolOut_,
      SinkPResultType,
      FinalResponsePResultType>({{#function:sink_has_first_response?}}std::move(_return.sinkConsumer),{{/function:sink_has_first_response?}}{{^function:sink_has_first_response?}}std::move(_return), {{/function:sink_has_first_response?}}std::move(executor));

  return {serializeResponse("{{> common/function_name}}", &prot, ctx, result), std::move(sinkConsumerImpl)};
{{#else}}
  ::{{service:qualified_namespace}}::{{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult result;
{{^type:void?}}
  result.get<0>().value = const_cast<{{function:cpp_return_type}}*>(&_return);
  result.setIsSet(0, true);
{{/type:void?}}
  return serializeResponse("{{> common/function_name}}", &prot, ctx, result);
{{/if function:bidirectional_stream?}}
}

template <class ProtocolIn_, class ProtocolOut_>
void {{service:parent_service_cpp_name}}AsyncProcessor::throw_wrapped_{{function:prefixed_name}}(
    apache::thrift::ResponseChannelRequest::UniquePtr req,
    [[maybe_unused]] int32_t protoSeqId,
    apache::thrift::ContextStack* ctx,
    folly::exception_wrapper ew,
    apache::thrift::Cpp2RequestContext* reqCtx) {
  if (!ew) {
    return;
  }
{{#if function:exceptions?}}
  ::{{service:qualified_namespace}}::{{service:parent_service_cpp_name}}_{{function:prefixed_name}}_presult{{#if (or function:sink? function:stream?)}}::InitialResponsePResultType{{/if}} result;
  constexpr bool kHasReturnType = {{#if function:initial_response?}}true{{#else}}false{{/if}};
  if (!::apache::thrift::detail::ap::insert_exn<kHasReturnType>(result, ew, [&]<typename Ex>(Ex&){
    if (ctx) {
      ctx->userExceptionWrapped(true, ew);
    }
    ::apache::thrift::util::appendExceptionToHeader(ew, *reqCtx);
    ::apache::thrift::util::appendErrorClassificationToHeader<Ex>(ew, *reqCtx);
  })) {
{{#else}}
  {
{{/if function:exceptions?}}
    apache::thrift::detail::ap::process_throw_wrapped_handler_error<
        ProtocolOut_>(ew, std::move(req), reqCtx, ctx, "{{> common/function_name}}");
    return;
  }
{{#function:exceptions?}}
  ProtocolOut_ prot;
  auto response = serializeResponse("{{> common/function_name}}", &prot, ctx, result);
  auto payload = std::move(response).extractPayload(
      req->includeEnvelope(),
      prot.protocolType(),
      protoSeqId,
      apache::thrift::MessageType::T_REPLY,
      "{{> common/function_name}}");
  payload.transform(reqCtx->getHeader()->getWriteTransforms());
{{#if function:bidirectional_stream?}}
  req->sendBiDiReply(std::move(payload), apache::thrift::detail::ServerBiDiStreamFactory::makeNoopFactory());
{{#else if function:stream?}}
  req->sendStreamReply(std::move(payload), apache::thrift::detail::ServerStreamFactory{nullptr});
{{#else if function:sink?}}
  req->sendSinkReply(std::move(payload), apache::thrift::detail::SinkConsumerImpl{});
{{#else}}
  return req->sendReply(std::move(payload));
{{/if function:bidirectional_stream?}}
{{/function:exceptions?}}
}
{{/function:oneway?}}
//
// End of Method '{{function:name}}'
//

{{/function:starts_interaction?}}{{/function:return_type}}{{/service:functions}}{{#service:functions?}}
//
// End of Service Methods
//
{{/service:functions?}}
